perm filename IOSAIL.BTH[UP,DOC]8 blob sn#392902 filedate 1978-11-04 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00018 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	Changes to IOSAIL.
C00010 00003	Introduction.
C00012 00004	SPACE,TAB,BELL,FORMFD,LF,CR,CRLF,!,PROC,REF,THRU,UPTO,BEGINLOOP.
C00016 00005	NULLSTRING,CVBS,ALPHA,NUMER,MEMLOC,LOWBOUND,UPBOUND,NUMDIM.
C00019 00006	DEBUG,BUGON,BUGOFF.
C00025 00007	YES,NO,NUMERIC,LOWERCASE,UPPERCASE,ALPHABETIC,ALPHAMERIC.
C00027 00008	CAPITALIZE.
C00028 00009	ROUND, FLOOR, CEILING.
C00030 00010	A discussion of channels.
C00033 00011	OPENREAD,OPENWRITE,CLOSEFILE,CLOSEALL.
C00036 00012	Filenames.
C00037 00013	The reads: LINE,INTEGER,REAL,STRING,WORD,YESNO,BUFFER,READ,RDTTY.
C00043 00014	EOF.
C00045 00015	GETPROMPT,SETPROMPT.
C00047 00016	SETREADER,GETREADER.
C00049 00017	SETECHO.
C00050 00018	TERM!PRINT!(ON | OFF),FILE!PRINT!(ON | OFF),FILE!PRINT!CLOSE.
C00052 ENDMK
C⊗;
Changes to IOSAIL.
 
∂11/4/78  -- OLD IOSAIL
The old iosail files (iosail.old, iosail.ohd, iosail.orl) have been
deleted.

∂10/24/78 -- Update on ROUND, FLOOR, CEILING
New procedure CEILING:  CEILING(X)=-FLOOR(-X).  New startcode bodies for
FLOOR and CEILING.  All three procedures are now simple.  Thanks to KS
for the new code for FLOOR and CEILING!

∂10/24/78 -- New Default Prompt
The default prompt for channels -1 and -2 is now " ?>".  (In order to be
compatable with LOTS.)

∂10/13/78 -- FLOOR FUNNTION.
There is now a FLOOR function that corresponds to ROUND.  FLOOR always
returns the standard default coersion of real to integer despite
compile time switches.  (The default rounds towards negative infinity:
-67.8 => -68, 3.5 => 3.)

∂7/7/78 -- Change in meaning for channels -1 and -2
To maintain consistency with the rest of SAIL channel -1 has now become
the terminal and -2 is the primary input channel.  This should be
transparent if your program does not explicitly refer to channel -1 or -2
(i.e. all of the commands that had -1 as the default now have -2).

The old version is maintained as follows:
	IOSAIL.SAI ==> IOSAIL.OLD
	IOSAIL.HDR ==> IOSAIL.OHD
	IOSAIL.REL ==> IOSAIL.ORL
	(all on [SUB,SYS]).
Therefore, to get the old version (which will eventually go away) use
	REQUIRE "IOSAIL.OHD[SUB,SYS]" SOURCE!FILE;
Please report all bugs to BTH.

∂6/8/78 -- WRITE FILE LOOKUP FIXED
A bug in iosail.sai which prohibited writefile retries has been fixed.
(LOOKUP→ENTER in the retry section.)

∂5/14/78 -- DOCUMENTATION JUSTIFICATION CLEANED UP
Up to this point there were a couple of justification patterns in
the text of IOSAIL.BTH[UP,DOC].  These were made consistent:
E format 10,0,74,-1.  (Note the Changes page was not changed.)

∂4/28/78 -- LOADER BUG FIXED
The names FILE!PRINT!ON,FILE!PRINT!OFF,FILE!PRINT!CLOSE are too long
to be distinguished by the loader (since they were moved to IOSAIL.SAI)
therefore the procedures were renamed to be !!FPON,!!FPOFF,!!FPC and
the long names became macros in .HDR calling these new names for
the procedures.  The change should be transparent to the user except
that the new bang-bang (!!) names of course become "reserved words".

∂4/27/78 -- FILE!PRINT!ON,FILE!PRINT!OFF,FILE!PRINT!CLOSE,CLOSEALL
have been moved from macros in .hdr to procedures in .sai to
permit PROFILE to make more reasonable output for these features.
In addition CLOSEALL now closes a SETPRINT file that may be open.

∂4/25/78 -- IOSAIL.DOC[107,bth] ==> IOSAIL.BTH[up,doc]
	    IOSAIL.HDR[107,bth] ==> IOSAIL.HDR[sub,sys]
	    IOSAIL.REL[107,bth] ==> IOSAIL.REL[sub,sys]
	    IOSAIL.SAI[107,bth] ==> IOSAIL.SAI[sub,sys]

∂2/23/78 -- openread and openwrite bug fixed
prompting for filenames in openread and openwrite will now
work correctly even with setprint in use (due to changing a
print to an outstr).

∂2/21/78 -- new macros
newr=new!record
nullr=null!record

∂2/21/78 -- ROUND
new ROUND function see iosail.doc

∂2/21/78 -- READINTEGER "fixed"
READINTEGER has been changed to use cvd rather that intscan since
it was found that intscan uses realscan and can lose some integers.

∂2/13/78 -- tab bug fixed
When the iosail package was brought from lots tabs were changed to spaces,
hence tabs were not passed over correctly in the input routine (eg. read
integer).  This has been corrected.

∂2/7/78 -- new macros
R!C = record!class
R!P = record!pointer

∂1/30/78 -- openread / openwrite fixed
if on opening a filename, that file does not exist then iosail
now will permit the user to change device as well as file name.

∂1/26/78 -- availability of rdtty
RDTTY is now available for use by the user of IOSAIL.

∂1/26/78 -- rdtty correction
RDTTY now prompts with OUTSTR to the terminal  which ignores
the status of setprint.

∂1/26/78 -- prompt changes
Instead of prompting with ?>integer> IOSAIL now prompts with ?>(integer)
and similarly for other types.

∂1/26/78 -- new READ
READ is no longer a synonym for READREAL.  If the prompting is still the
default then READ will prompt with >? instead of ?>.  This is for
compatibility with Floyd's notes.

∂1/26/78 -- READBUFFER
a new string procedure that returns anything left in the input buffer.

∂1/23/78 -- new macros
SPACE = " ".
NEWPAGE = FORMFEED.
 
∂1/23/78 -- rdtty
Rdtty has been incorporated into IOSAIL and setty has been eliminated.
Introduction.



	  This document describes the  SAIL procedures and macros  written
by Kevin Karplus <k.kjk> December 1977.   The package is intended for  the
CS105 and  CS106  classes, but  is  available to  all  users of  the  SAIL
language.  The  package is  being maintained  at SU-AI  by Brent  Hailpern
(BTH).  All bugs and suggetions should be sent to him.

To get all the macros and  procedures declared, put the statement  

REQUIRE "IOSAIL.HDR[SUB,SYS]" SOURCE!FILE;

in the declarations section of the  outermost block of your SAIL  program.
This will automatically include IOSAIL.REL[SUB,SYS]  in the list of  files
for  the   LOADER.   The   SAIL  sources   for  the   procedures  are   in
IOSAIL.SAI[SUB,SYS],    the    macros    and    declarations    are     in
IOSAIL.HDR[SUB,SYS].

	  There are several distinct parts to this package:  macros  whose
function is that of abbreviation,  some macros for debugging, some  simple
procedures for frequently used character tests, the input/output routines,
and some macros for use with SETPRINT.
SPACE,TAB,BELL,FORMFD,LF,CR,CRLF,!,PROC,REF,THRU,UPTO,BEGINLOOP.
	(MACROS defined in IOSAIL.HDR[SUB,SYS])

	  The following are simple  abbreviations that have proven  useful
in the past.

Printing abbreviations:
TAB            ==> a string containing the tab character.
SPACE          ==> a string containing the space character.
BELL           ==> a string containing the bell character.
FORMFEED       ==> a string containing the formfeed character ↑L.
NEWPAGE = FORMFD = FORMFEED
LF = LINEFEED  ==> a string containing the linefeed character.
CR             ==> a string containing a carriage return.
CRLF = NEWLINE ==> the equivalent of CR & LF.

Stylistic abbreviations:
!              ==> COMMENT
PROC           ==> PROCEDURE
REF            ==> REFERENCE
THRU           ==> step 1 until
UPTO           ==> :=1 step 1 until
BEGINLOOP      ==> while true do begin

Record/Pointer abbreviations:
R!C            ==> record!class
R!P            ==> record!pointer
NEWR	       ==> new!record
NULLR	       ==> null!record

LOTS plotting commands (included for compatibility):
PLOTLF         ==> a string containing ↑E & LINEFEED


	  Note that the BEGINLOOP macro along with the DONE construct  can
be used  as  a looping  construct  of  great power  and  simplicity.   For
example:
     beginloop "example loop"
       temp:=READLINE;
       if EOF then DONE "example loop";
       !   do here whatever you want with temp;
                .
                .
                .
     end "example loop";
this program  fragment reads  lines  of the  main  input device  until  it
reaches an end  of file.  All  the lines  read are processed  by the  code
after the IF statement, and the the loop is terminated by satisfaction  of
the end of  file test.   (The procedures  READLINE and  EOF are  described
later.)  The capitalization of the  DONE statement is encouraged, to  make
it stand out from the predominantly lower case program body.

	  Please remember  that the  macro for  comment (!)   must have  a
delimiter after it  to be recognized  as a character.   Thus "!ABCD" is  a
legal variable, but "!  ABCD" is a comment.

	  The usual uses  of FORMFEED, TAB,  BELL, and CRLF  are with  the
PRINT statement to print them on your terminal (or with similar statements
to add them to files).  You will find CRLF the most useful of this set  of
characters.  FORMFEED at the begining of a line tells the line printer  to
go to a new page before printing the line.

NULLSTRING,CVBS,ALPHA,NUMER,MEMLOC,LOWBOUND,UPBOUND,NUMDIM.
	(MACROS defined in IOSAIL.HDR[SUB,SYS])


     Some other macros of interest are
NULLSTRING(x)  returns TRUE if the string x is  the  null  string  and
                   FALSE otherwise.
CVBS(x)        converts boolean to string "TRUE" or "FALSE"
ALPHA          ==> a string containing the  entire  alphabet  in  both
                   upper and lower cases.
NUMER          ==> a string containing all the digits.
MEMLOC(x)      ==> memory[location(x)].  (useful only to hackers)
LOWBOUND(arr,dim) returns the lower bound of the  DIMth  dimension  of
                   the array ARR at runtime.
UPBOUND(arr,dim) returns the upper bound of the DIMth dimension of the
                   array ARR at runtime.
NUMDIM(arr)    returns the  number  of  dimensions  of  array  ARR  at
                   runtime.


	  ALPHA and NUMER have many uses, one of the most important  being
to make it  easier to set  up "breaktables" for  the SAIL SCAN  statement.
Novices should  not have  to  worry about  this powerful  but  complicated
statement, as the routines described later should be sufficient for  their
needs.

	  LOWBOUND, UPBOUND, and NUMDIM are useful if you use arrays  with
variable dimensions, but  need to know  the dimensions at  runtime to  set
loop  parameters.   These  macros   are  particularly  useful  inside   of
procedures, which can then be handed arrays of any size and still be  able
to figure out what to do with them.  Note that NUMDIM will be negative for
string arrays (because of the expansion to the ARRINFO command).
DEBUG,BUGON,BUGOFF.

	  This  is   a  very   powerful  macro   originally  designed   by
J.Q.Johnson, later slightly modified by Kevin Karplus.  It allows the user
to print out  selected variables  at any point  in a  program, along  with
identification of  the variables  and the  location in  the program.   The
output looks like:

DEBUG:after fiddling the foofram i=17.3; j=99.99; stq="jjjgh"jjitm";

which could have been created by a program segment like:
      i:=17.3;
      j:=99.99;
      stq:="jjjgh""jjitm";
      DEBUG(after fiddling the foofram,(i,j,stq));
Notice that the quote mark inside the string is not doubled by DEBUG,  and
that  the  location  identification  is  any  string  of  characters  that
satisfies the normal rules about arguments to macros.  For simplicity,  it
is best to keep this to a few short simple words, without commas or  other
punctuation.

	  Like most macros, DEBUG gets  confused by commas that it  thinks
are superfluous  or  misplaced.   Thus  a  statement  like:   DEBUG(before
input,(innersecrets[1,2,j])); is liable to give you an error message (or a
mysterious DRYROT).  SAIL doesn't know  about DEBUG, so the error  message
will be exceedingly unhelpful.  There is, of course, a way around.   (SAIL
abounds in  ways  to get  around  things, it  adds  to its  mystery.)  The
particular hack involved is to put  curly braces around the variable  that
is   causing   the   difficulty.    Thus   the   statement    DEBUG(before
input,({innersecrets[1,2,j]},j)); would work.

	  It is also possible to  use expressions instead of variables  as
the things to be printed.  They will be printed as if they were in a PRINT
statement , and will  be labeled with the  expression that produced  them.
Use of expressions  is not guaranteed  by the implementors,  so don't  get
upset if it doesn't work.

	  As a further convenience, if you have only a single variable  to
print, you can omit the parentheses around it.  "DEBUG(here,a);" should be
exactly the same as "DEBUG(here,(a));" .

	  Sometimes great  quantities of  debug output  can be  generated,
even when it is known  that the bug you are  looking for only occurs  when
certain special conditions  occur.  While  the computer  cannot check  the
phase of the moon  or the absence of  the TA, it is  often easy to put  in
checks to see whether the  program has gotten a  value that is strange  at
some point, and then turn on  the DEBUG statements.  To make this  easier,
two macros  have been  included:  BUGON  and BUGOFF.   Normally the  DEBUG
statements are "on" and print output on the terminal.  Execution of BUGOFF
turns off the DEBUG statements until the next execution of a BUGON.   Note
that the  variable  !bugoff  is  what is  changed  by  these  macros,  you
shouldn't use that variable name for anything else.  As an example, assume
that the routine BUGGY is known to die when it has to handle long strings.
Then  something  like  this  fragment  might  help:   
	BUGOFF;
	.  .  .  
	if length(arg) > 56 then BUGON else BUGOFF;
	BUGGY(arg);
	. . .

	  There is  also  a  more  general  and  more  powerful  debugging
facility available, called  BAIL.  For  most short  student programs,  the
effort involved in learning and using  BAIL is not justified by the  small
extra debugging power it gives you.
YES,NO,NUMERIC,LOWERCASE,UPPERCASE,ALPHABETIC,ALPHAMERIC.
Some small simple procedures for dealing with strings

	  This section  contains  a  number of  very  short,  very  simple
procedures that are  frequently useful.  All  are boolean procedures  (ie.
they return either TRUE or FALSE).

	  The argument to all the procedures is a character.  This can  be
done either by  having the character  be the first  character of a  string
passed to the procedure, or by passing an integer whose value is the ASCII
representation of the character.  Passing  a string for interpretation  of
its first character is probably more common for most of these procedures.


YES(char) returns TRUE if the character is "Y" or "y".

NO(char) returns TRUE if the character is "N" or "n".

NUMERIC(char) returns TRUE if the character is a decimal digit.

LOWERCASE(char) returns TRUE if the character is a lower case letter.

UPPERCASE(char) returns TRUE if the character is an upper case letter.

ALPHABETIC(char) returns TRUE if the character is a letter.

ALPHAMERIC(char) returns TRUE if the character is either a letter or a
          digit.
CAPITALIZE.

CAPITALIZE(string) returns its string argument with all the lower case
          letters  converted  to  upper case (or, to be old fashioned,
          all  the  miniscules   converted   to   magiscules).    Thus
          CAPITALIZE("abcDEFmmm123 $ $")  returns  "ABCDEFMMM123 $ $".
          Note that the argument is left unchanged.
ROUND, FLOOR, CEILING.
 
SAIL's default coersion from real to integer is the floor function.  It
can be changed by a compile time switch.  Floor rounds toward neagtive
infinity, so that 134.5 => 134 and -67.999 => -68.

The FLOOR procedure always returns this default floor function no matter
how the compile switch was set.  (It uses a more efficient procedure than
the default coercion).

CEILING is a procedure that returns  -FLOOR(-X).

The ROUND procedure does FLOOR(x+.5) for real to integer coersion (it uses
the FIXR FAIL command).
 
To be used:
i:=ROUND(x);
i:=FLOOR(x);
i:=CEILING(x);
where i is integer and x is real.

A discussion of channels.

	  Whenever input  and output  are mentioned  in respect  to  SAIL,
channels are almost certain to be mentioned.  A channel (in this  context)
is simply a small number that gets associated with the file or device  you
are communicating with, so it isn't  necessary to refer to the whole  name
of the file and look for it each time you want to access it.  The  numbers
are rather arbitrarily assigned, and have no intrinsic meaning.

	  SAIL can  handle  about 16  channels  (0 through  15)  plus  the
controlling terminal (also known  as TTY:) as channel  -2.  Channel -1  is
also the terminal.  The channels used by this package are the same as SAIL
channels except:

     1.  When inputing through the procedures of this package, channel
         -2  means  the  "main reader channel".  This is the terminal,
         unless SETREADER is called to change it.

     2.  When inputting though the procedures of this package, channel
         -1 means the controlling terminal.

     3.  All the SAIL functions behave as normal, do NOT try printing on 
	 -1 nor should you try printing on channel -2.  To output to the
	 terminal use PRINT.

     4.  The normal mechanisms for opening and  closing  files  should
         not  be  employed  if the files are to be used with the other
         routines of this package.
OPENREAD,OPENWRITE,CLOSEFILE,CLOSEALL.

	  The process of finding the file you want to use and  associating
a channel number with it is called "opening a file".  The similar  process
of cleaning out the buffers and putting the file away when you are done is
called "closing a file".

	  There are  two routines  in IOSAIL  for opening  a file,  called
OPENREAD and OPENWRITE.  Both take the  file name as an argument and  both
return the channel associated with the file.  If no filename is given, (or
if the null string is given) then the OPEN...  procedures ask for the file
name from the  teletype.  Note that  TTY:  is an  acceptable filename  for
both input and output (it means  the terminal you are running the  program
from).

The format of the commands looks like:
chan:=OPENREAD;
chan:=OPENREAD("FOO.in");
chan:=OPENWRITE;
chan:=OPENWRITE("FOO.OUT");

	  Please resist the temptation to use the .SAI extension for  your
output files.  It is very easy to destroy programs that way.

	  The counterpart  of "opening",  "closing",  is handled  just  as
easily.  CLOSEFILE(chan) will close the  file and free the channel  number
for reassignment.  CLOSEFILE(-2) will close the reader channel and  return
channel -2 to its original status as the controlling input terminal.

	  There is a procedure CLOSEALL, which will close all the channels
that are open (including any setprint file), and generally try to set  the
input output system as much like its initial state as it can.  To do  this
massive clean-up, make:

CLOSEALL;

the last executable statement of your program.
Filenames.

	  It is possible to determine the  filename of a channel that  has
been opened by use of the string procedure FILENAME(chan).  If the channel
is connected to a terminal the string "TTY:" is returned.
The reads: LINE,INTEGER,REAL,STRING,WORD,YESNO,BUFFER,READ,RDTTY.
Procedures for input

	  There are several procedures for inputting various values from a
channel.  All of them have the same basic calling sequence, and behave  in
similar manners (except for  RDTTY which is  described below).  They  are:
READLINE, READINTEGER,  READREAL, READSTRING,  READWORD, READYESNO,  READ,
READBUFFER.

The calling sequence is
foo:=READ...(chan);
foo:=READ...;
where READ...  means any of the READ routines mentioned above, and foo  is
of the appropriate  type for the  particular routine.  If  chan is  given,
then the channel must be  open for reading.  If chan  is not given, -2  is
assumed and reading is from the reader channel.

	  READLINE is the most basic of  these routines, and is called  by
all the others.  It reads a line (ignoring carriage return) until it finds
a linefeed, or end-of-file is reached.  If  there are no more lines to  be
read from the file, a null  string is returned, otherwise the string  read
(without crlf) is returned.

	  READINTEGER and  READREAL ignore  blank, tabs,  and crlfs  until
they get to a non-blank character.  If the character is not acceptable for
the first character of  a number, an error  message is generated.  If  the
channel is connected to a terminal, then the user is prompted for a retry.
Otherwise the error is fatal, and the program aborts.

	  READ is the same as READREAL except that if the prompting on the
channel is the default then the prompting for that particular read becomes
>?  instead of ?>.

	  READSTRING reads a  string beginning and  ending with the  quote
character (").  A  quote can  be included in  the string  by doubling  it.
Thus the string  "abc""DEF""ghi" would  be read as  abc"DEF"ghi .  Blanks,
tabs, and crlfs before the opening " are ignored.  If the first  character
after the blanks  is not a  quote, an error  message is printed.   (Again,
fatal if not TTY, retry if TTY).

	  READWORD reads the next punctuation mark or string of alphameric
characters.  Leading blanks, tabs, and crlfs are ignored.

	  READYESNO expects the  next character (after  blanks, tabs,  and
crlfs) to be "Y","y", "N", or "n".  It then returns TRUE if it is a y  and
FALSE if it is an n.  Any other character results in an error.  (Fatal  if
not a TTY, retry if a tty).

	  READBUFFER returns the unread portion  of the input buffer as  a
string.

	  RDTTY is called  by RDTTY("prompt"), where  ">" is default.   It
prints that prompt on the terminal  (ignoring SETPRINTs) and then reads  a
line from the terminal  and returns it  as a string.   RDTTY is faster  in
that  it  avoids   all  overhead  of   READLINE(-1)  and  permits   direct
specification of the  prompt.  Its drawback  is that it  bypasses the  eof
mechanism for the terminal and should therefore be used with care.

	  Note:  READLINE always reads a  fresh line from the terminal  or
file, all the  other routines  will keep reading  on the  same line  until
there is nothing  left of the  line, or a  READLINE is done.   Thus it  is
possible to read  every non-blank  of a  file with  repeated READWORD,  or
every number with READREAL.  Line numbers in files are ignored.
EOF.

End-of-file handling

	  When one  of the  READ...  routines  is called,  and can't  find
anymore input, it returns  an appropriate null  value.  (Null strings  for
string valued  procedures,  FALSE  for  READYESNO,  and  intscan(null)  or
realscan(null) for READINTEGER and READREAL).   The EOF indication can  be
tested with the procedure EOF(chan).  As  with all the procedures, if  the
channel number is omitted,  channel -2 is assumed.   EOF will return  TRUE,
after the call which returned the null value.

Thus the correct test for end-of-file is:
    BEGINLOOP "readloop"
    temp:=READ...;
    if EOF then DONE "readloop";
    !  body of loop;
          .
          .
          .
    END "readloop"

	  Testing for end-of-file  should be  done after  every read.   An
end-of-file for display can be accomplished by typing  <control><meta><lf>
(or <ctrl>z  from  a  TTY).   However,  only  one  null  return  is  made,
subsequent calls to read  from a channel associated  with a terminal  will
behave normally, and EOF  will only return  TRUE if the  last read on  the
channel hit the EOF character.


GETPROMPT,SETPROMPT.

Prompting 

	  When reading from the terminal, the user is prompted for  input.
The default prompt for channels -2 and -1 is "?>", for the other  channels
it is  the channel  number followed  by  ">".  The  prompt string  can  be
determined by foo:=GETPROMPT(chan), and changed by SETPROMPT(string,chan).

	  If the channel is not specified, then channel -2 is assumed.  If
the string to SETPROMPT is  null then the prompt will  be set back to  the
default.

	  If the promptstring for  a channel is  the default string,  then
the routines READINTEGER, READREAL, READSTRING, READWORD, READYESNO add  a
further prompt to indicate what type of input is being requested.
SETREADER,GETREADER.

	  The use of channel -2 to access the main reader channel has been
mentioned before.  To associate  -2 with some real  channel is very  easy.
First,  OPENREAD   should  be   called  to   create  the   channel,   then
SETREADER(chan) will  make  chan  the main  reader  channel.   The  reader
channel can  be  reset  to  the terminal  (the  initial  state)  by  doing
SETREADER(-2).   SETREADER   without   an  argument   is   equivalent   to
SETREADER(-2).

	  GETREADER is an integer valued procedure with no arguments which
returns the channel number  of the main reader  channel.  If no  SETREADER
call has been made, then GETREADER returns -2.


SETECHO.

Echoing

	  All the input through the main reader channel can be echoed to a
file, or to the terminal.  This  is accomplished by doing an OPENWRITE  to
open a channel,  and SETECHO(chan)  to send  the echoing  to the  channel.
SETECHO(-2) or SETECHO without arguments, turns off the echoing.  The echo
can be forced to the terminal by SETECHO(-1).

	  Note that  you  cannot echo  to  the  file used  in  a  SETPRINT
statement, because  of the  way  SAIL handles  SETPRINT.  However,  it  is
possible to do CPRINT to the same channel as the echoing.

TERM!PRINT!(ON | OFF),FILE!PRINT!(ON | OFF),FILE!PRINT!CLOSE.

Setprint control macors and procedures

	  There are two  macros and  three procedures in  this class,  and
they all invoke  SETPRINT with  various arguments.  They  serve to  direct
your PRINT output to your terminal, a file, or both.  Normally your  PRINT
output goes just to your terminal, so you have to do something special  if
you want the output to go to a file as well.  You could open a channel and
write  to  it  (using  OPENWRITE   and  CPRINT),  but  this   necessitates
duplicating the PRINT statements and is often not worth the trouble.

	  First   the    file    should   be    established    with    the
SETPRINT(filename,"B");  statement  as  described  int  the  SAIL  manual.
Thereafter TERM!PRINT!ON,  TERM!PRINT!OFF, FILE!PRINT!ON,  FILE!PRINT!OFF,
FILE!PRINT!CLOSE should suffice for handling the various SETPRINT options.

	  Note:   the  FILE!PRINT!...  identifiers  are  actually   macros
calling the  three procedures  !!FPON,!!FPOFF,!!FPC so  avoid using  these
bang-bang (!!)  identifiers as variables in your programs.